def build_home
  $model.start_operation "build_home"

  # instantiate home
  home = Home.new(JoistBasedTechnology.new, 1, 30.m, 10.2.m)

  #make some manual changes
  #home.walls['s'] = GlassFacade.new home
  #home.walls['w'] = GlassFacade.new home
  #home.walls['n'] = GlassFacade.new home
  #home.walls['e'] = GlassFacade.new home

  # display
  home.make_group($model.entities)
  
  $model.commit_operation
  
  # print stats
  print_stats( home.living_area_m2 )

end

World_directions = ['w', 'e', 's', 'n']

class Home < Debuggable
  
  attr_accessor :floors, :walls, :dim_x, :dim_y, :technology, :living_area_m2

  def initialize( technology, floors_cnt, dim_x, dim_y )
    @technology = technology
    @dim_x = dim_x
    @dim_y = dim_y
    
    @floors = Array.new floors_cnt
    for f in 0..@floors.size-1
      @floors[f]= Floor.new self
    end
    
    # walls ordering w,e,s,n is important for dynamic code calculating living area and wall supporting offsets, 
    # do not change this
    @walls = Hash.new
    World_directions.each {|world_direction|
      @walls[world_direction]=OuterLoadBearingInsulatedWall.new self
    }
  end
  
  def make_group(entities)    
    #name = 'home_'+@floors.size.to_s+"_"+dim_to_s(@dim_x)+"_"+dim_to_s(@dim_y)
    
    group=entities.add_group

    @living_area_m2 = 0;
    
    @floors.each do |floor|
      floor_name = floor.make_group(group.entities)
    end
    
    return group
  end
end

class Wall < Debuggable
  attr_accessor :home, :thickness, :support_offset
  
  def initialize(home)
    @home = home
    @deb_flag = @home.deb_flag
  end
  
  def technology()
    return @home.technology
  end
end

class OuterLoadBearingInsulatedWall < Wall
  def initialize(home)
    super(home)
    @thickness = technology.externalWallThickness
    @support_offset = technology.inner_wall_thickness
  end
end

class GlassFacade < Wall
  def initialize(home)
    super(home)
    @thickness= technology.joist_width / 2
    @support_offset = technology.joist_width / 2
  end
end

class Floor
  
  attr_accessor :home, :living_area_m2
  attr_accessor :living_area_w_support_offset, :living_area_e_support_offset, :living_area_s_support_offset, :living_area_n_support_offset
  attr_accessor :living_area_w, :living_area_e, :living_area_s, :living_area_n, :living_area_x, :living_area_y
  attr_accessor :platform_w, :platform_e, :platform_s, :platform_n, :platform_x, :platform_y
  attr_accessor :cealing_z_end
  
  # some strict private variables 
  @group
  
  def initialize(home)
    @home = home
    @cealing_z_end=0
  end

  def technology()
    return @home.technology
  end  

  def set_edge_vars( world_direction, start_coordinate, plus_or_minus )
      wall = @home.walls[world_direction]
      # living_area_[w|e|s|n]_support_offset
      eval("@living_area_"+world_direction+"_support_offset = -"+plus_or_minus+wall.support_offset.to_f.to_s)
      debug( "@living_area_"+world_direction+"_support_offset: "+eval("@living_area_"+world_direction+"_support_offset.to_mm.to_s")+" mm")
      # @living_area_[w|e|s|n]
      eval("@living_area_"+world_direction+" = "+start_coordinate.to_f.to_s+plus_or_minus+"wall.thickness.to_f")
      debug( "@living_area_"+world_direction+": "+eval("@living_area_"+world_direction+".to_mm.to_s")+" mm")
      # patform_[w|e|s|n]
      eval( "@platform_"+world_direction+" = (@living_area_"+world_direction+"+"+"@living_area_"+world_direction+"_support_offset).round")
      debug( "@platform_"+world_direction+": "+eval("@platform_"+world_direction+".to_mm.to_s")+" mm")
  end

  def make_group(entities)
    #name = 'floor_'+@home.floors.size.to_s+"_"+dim_to_s(@home.dim_x)+"_"+dim_to_s(@home.dim_y)
    @group = entities.add_group
    t = @home.technology
    
    ######################################
    # determine coordinates of living area and ....
    ######################################
    # determine edge-offsets for wall-supports ( floor-wall overlaps )
    # offsets are positive or negative, depending on offset direction
    ######################################
    # variable declarations are not necessary, but for code auto-completition quite useful
    # 
    
    set_edge_vars('w', 0,           '+')
    set_edge_vars('e', @home.dim_x, '-')
    set_edge_vars('s', 0,           '+')
    set_edge_vars('n', @home.dim_y, '-')

    @living_area_x=@living_area_e.to_mm-@living_area_w.to_mm
    @living_area_y=@living_area_n-@living_area_s
    @platform_x=platform_e-platform_w
    @platform_y=platform_n-platform_s
    debug("living_area_x: "+@living_area_x.to_mm.to_s+", "+"living_area_y: "+@living_area_y.to_mm.to_s)

    @living_area_m2= (@living_area_e-@living_area_w).to_m * (@living_area_n-@living_area_s).to_m
    debug( "platform's @living_area_m2: "+@living_area_m2.to_s )
    @home.living_area_m2 += @living_area_m2
    
    #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # add construction elements from bottom to up
    #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    
    ################################################
    # add floor plates
    ################################################
    add_floor_plates
    
    ################################################
    # add horisontal beams parallel to X axis ( X beams )
    ################################################
    add_horisontal_x_beams
    
    ################################################
    # add horisontal Y beams
    ################################################
    add_horisontal_y_beams
    
    return @group
  end
  
  def add_floor_plates()
    x=@living_area_w    
    while x < @living_area_e
      y=@living_area_s
      while y < @living_area_n-Tollerance
        dxy = Geom::Transformation.translation [ x, y, 0 ]
        
        # cealing
        dz = Geom::Transformation.translation [ 0, 0, 0 ]
        plate_name = make_block_component2( 'cealing_plate', (min @living_area_e-x, technology.plates_dim_a), (min @living_area_n-y, technology.plates_dim_b), technology.cealing_plates_strength, $materials['gips_1'] )
        @group.entities.add_instance( $model.definitions[plate_name], dxy*dz )
        @cealing_z_end = technology.cealing_plates_strength
        # make space for joists
        floor_z_start = @cealing_z_end + technology.joist_heigth
        
        # main floor bearing plates
        dz  = Geom::Transformation.translation [ 0, 0, floor_z_start ]
        plate_name = make_block_component2( 'floor_plate', (min @living_area_e-x, technology.plates_dim_a), (min @living_area_n-y, technology.plates_dim_b), technology.floor_plates_strength, $materials['OSB_1'] )
        @group.entities.add_instance( $model.definitions[plate_name], dxy*dz )

        y += technology.plates_dim_b
      end
      x += technology.plates_dim_a
    end
  end

  def add_horisontal_x_beams()
    tr=Geom::Transformation.translation [ @platform_w+technology.joist_width, technology.joist_width/2, @cealing_z_end ];
    rot_x = Geom::Transformation.rotation ORIGIN, [1,0,0], Math::PI/2
    rot_y = Geom::Transformation.rotation ORIGIN, [0,1,0], -Math::PI/2
    y=@living_area_s
    
    beams_cnt=0
    #last_y is needed to check minimal distance between beams, initialized with negative value
    last_y = -1
    last_x = -1
    
    joist_name = make_joist("joist", technology.joist_width, technology.joist_heigth, @platform_x-2*technology.joist_width)

    # add last X beam supporting wall if needed
    if @platform_s < @living_area_s-technology.joist_width-Tollerance
      dxy = Geom::Transformation.translation [ 0, platform_s+technology.joist_width/2, 0 ]
      @group.entities.add_instance( $model.definitions[joist_name], tr*dxy*rot_x*rot_y )
    end

    while true
      dxy = Geom::Transformation.translation [ 0, y, 0 ]
      @group.entities.add_instance( $model.definitions[joist_name], tr*dxy*rot_x*rot_y )
      beams_cnt += 1;
      odd = beams_cnt % 2
      # determine next Y-position
      if y <= @living_area_n - technology.plates_dim_b/2 - odd * technology.plates_dim_b/2 # far from the end, odd beams jons plates, so they must go into raster-based position
        y += technology.plates_dim_b/2
      elsif y < @living_area_n - technology.plates_dim_b/2 # near to the end
        y += (@living_area_n-y)/2
      elsif y < @living_area_n # last beam
        y = @living_area_n
      else
        break
      end
      if y-last_y < technology.joist_width
        raise Exception, "beams distance "+(y-last_y).to_mm.to_s+" mm is less then beam width, home dimension in y direction must be changed"
      end
      last_y=y
    end

    # add last X beam supporting wall if needed
    if @platform_n > @living_area_n+technology.joist_width+Tollerance
      dxy = Geom::Transformation.translation [ 0, @platform_n-technology.joist_width/2, 0 ]
      @group.entities.add_instance( $model.definitions[joist_name], tr*dxy*rot_x*rot_y )
    end
      
  end

  def add_horisontal_y_beams()
    tr=Geom::Transformation.translation [ @platform_w, @platform_s, @cealing_z_end ];
    rot_x = Geom::Transformation.rotation ORIGIN, [1,0,0], Math::PI/2
    #rot_y = Geom::Transformation.rotation ORIGIN, [0,1,0], -Math::PI/2
    #x=living_area_w-technology.inner_wall_thickness
    joist_name = make_joist("joist", technology.joist_width, technology.joist_heigth, @platform_y)
    # first Y beam
    dxy = Geom::Transformation.translation [ 0, 0, 0 ]
    @group.entities.add_instance( $model.definitions[joist_name], tr*dxy*rot_x )
    # last Y beam
    dxy = Geom::Transformation.translation [ @platform_x-technology.joist_width, 0, 0 ]
    @group.entities.add_instance( $model.definitions[joist_name], tr*dxy*rot_x )
  end
end
